/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.miit.elearning.hibernate.dao;

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import ru.miit.elearning.hibernate.dto.Account;
import ru.miit.elearning.hibernate.dto.CurriculumLevel;
import ru.miit.elearning.hibernate.dto.Profile;
import ru.miit.elearning.hibernate.dto.ProfileId;
import ru.miit.elearning.hibernate.dto.ProfileType;
import ru.miit.elearning.hibernate.dto.UserIdentity;
import ru.miit.elearning.servlets.security.CreateJDBCRealmUser;

/**
 *
 * @author Cyclone
 */
public class UserDAO extends GenericDAO {

    public UserDAO(Session session) {
        super(session);
    }

    public List<UserIdentity> findUsersByPage(int min, int count, String sort) {
        min = Math.max(min, 0);
        String query = "from UserIdentity";
        query += prepareSort(sort);

        return session.createQuery(query).setFirstResult(min).setMaxResults(count).list();
    }

    private String prepareSort(String sort) {
        if (sort == null || sort.trim().equals("") || sort.trim().equals("null")) {
            sort = "name";
        } else {
            if (sort.trim().equals("email")) {
                sort = "login";
            }
        }
        return " order by " + sort;
    }
    private String findUsersByPageByTutorQuery = "from UserIdentity ui "
            + "where ui.account in ( "
            + "select distinct ud2.id.account "
            + "from UserData ud2 "
            + "where ud2.id.curriculumLevel in "
            + "(select cl.id "
            + "from CurriculumLevel cl, UserData ud "
            + "where ud.id.account = :tutorId "
            + "and ud.id.profileType = " + PROFILE_TYPE_TUTOR
            + "and cl.id = ud.id.curriculumLevel) "
            + "and ud2.profile.profileType.id = " + PROFILE_TYPE_STUDENT + ")";

    public long countUsersByTutor(int tutorId) {
        return (Long) session.createQuery("select count(ui) " + findUsersByPageByTutorQuery).setParameter("tutorId", tutorId).uniqueResult();
    }

    public List<UserIdentity> findUsersByPageByTutor(int min, int count, String sort, int tutorId) {
        min = Math.max(min, 0);

        String query = findUsersByPageByTutorQuery;

        query += prepareSort(sort);

        return session.createQuery(query).setParameter("tutorId", tutorId).setFirstResult(min).setMaxResults(count).list();
    }

    public long countUsers() {
        return (Long) session.createQuery("select distinct count(user) from UserIdentity user").uniqueResult();
    }

    public List<CurriculumLevel> findCoursesByStudent(String remoteUser) {
        return findCoursesByStudentId(findUserIdByLogin(remoteUser));
    }

    public List<CurriculumLevel> findCoursesByStudentId(int userId) {
        return findCoursesByUserId(userId, PROFILE_TYPE_STUDENT);
    }

    public List<CurriculumLevel> findCoursesByTutorId(int userId) {
        return findCoursesByUserId(userId, PROFILE_TYPE_TUTOR);
    }

    private List<CurriculumLevel> findCoursesByUserId(int userId, short profileType) {
        return session.createQuery("from CurriculumLevel cl where cl.id in "
                + "(select ud.curriculumLevel.id from "
                + "UserData ud "
                + "where ud.id.account = :userId "
                + "and ud.profile.profileType.id = :profileType "
                + "and ud.curriculumLevel.curriculumLevelType.id = :curriculumLevelType) "
                + "order by cl.title").setParameter("userId", userId).setParameter("profileType", profileType).setParameter("curriculumLevelType", CURRICULUM_LEVEL_TYPE_COURSE).list();
    }

    public List<CurriculumLevel> findCoursesByStudentIdByTutor(int studentId, int tutorId) {
        return session.createQuery("from CurriculumLevel cl where cl.id in "
                + "(select ud.curriculumLevel.id from "
                + "UserData ud "
                + "where ud.id.account = :tutorId "
                + "and ud.profile.profileType.id = :tutorType "
                + "and ud.curriculumLevel.curriculumLevelType.id = :curriculumLevelType) "
                + "and cl.id in "
                + "(select ud.curriculumLevel.id from "
                + "UserData ud "
                + "where ud.id.account = :studentId "
                + "and ud.profile.profileType.id = :studentType "
                + "and ud.curriculumLevel.curriculumLevelType.id = :curriculumLevelType) "
                + "order by cl.title").setParameter("tutorId", tutorId).setParameter("tutorType", PROFILE_TYPE_TUTOR).setParameter("studentId", studentId).setParameter("studentType", PROFILE_TYPE_STUDENT).setParameter("curriculumLevelType", CURRICULUM_LEVEL_TYPE_COURSE).list();
    }

    public List<CurriculumLevel> findSharedStudentsCourses(int studentId1, int studentId2) throws Exception {
        //new UserData().getId().getCurriculumLevel();
        return session.createQuery("from CurriculumLevel cl where cl.id in (select ud.id.curriculumLevel "
                + "from UserData ud "
                + "where ud.id.account = :studentId1 "
                + "and ud.profile.profileType.id = :studentType) "
                + "and cl.id in (select ud.id.curriculumLevel "
                + "from UserData ud "
                + "where ud.id.account = :studentId2 "
                + "and ud.profile.profileType.id = :studentType)").setParameter("studentId1", studentId1).setParameter("studentId2", studentId2).setParameter("studentType", PROFILE_TYPE_STUDENT).list();
    }

    public UserIdentity findUserById(int userId) {
        return (UserIdentity) session.createCriteria(UserIdentity.class).add(Restrictions.eq("account", userId)).uniqueResult();
    }

    public List<ProfileType> findAllProfileTypes() {
        return session.createQuery("from ProfileType").list();
    }

    public List<ProfileType> findProfileTypesByNoUser(int userId) {
        return session.createQuery("from ProfileType where id not in "
                + "(select profileType.id "
                + "from Profile "
                + "where account.id = :userId)").setParameter("userId", userId).list();
    }

    public UserIdentity addStudent(String email, String password, String firstname, String lastname) throws NoSuchAlgorithmException {
        List<Integer> studentTypeList = new ArrayList<Integer>();
        studentTypeList.add(new Integer(PROFILE_TYPE_STUDENT));
        return addUser(email, password, firstname, lastname, studentTypeList);
    }

    public UserIdentity addUser(String email, String password, String firstname, String lastname, List<Integer> profileTypeList) throws NoSuchAlgorithmException {
        int id = (Integer) session.save(new Account());

        password = CreateJDBCRealmUser.encryptPassword(password);

        UserIdentity userIdentity = new UserIdentity();
        userIdentity.setLogin(email);
        userIdentity.setName(lastname + " " + firstname);
//        userIdentity.setName(firstname + " " + lastname);
        userIdentity.setPassword(password);

        userIdentity.setAccount(id);
        session.save(userIdentity);

        for (Integer profileTypeId : profileTypeList) {
            Profile profile = new Profile();
            profile.setId(new ProfileId(profileTypeId.shortValue(), id));
            profile.setRecordDate(Calendar.getInstance().getTime());
            session.save(profile);
        }

        return (UserIdentity) session.get(UserIdentity.class, id);
    }

    public String deleteUser(int id) {
        Account account = (Account) session.get(Account.class, id);
        String name = null;
        try {
            name = ((UserIdentity) (account.getUserIdentities().toArray()[0])).getName();
        } catch (Exception e) {
            e.printStackTrace();
        }
        session.delete(account);
        return name;
    }

    public void updateUser(int userId, String name, String login) {
        UserIdentity userIdentity = (UserIdentity) session.get(UserIdentity.class, userId);

        userIdentity.setName(name);
        userIdentity.setLogin(login);

        session.update(userIdentity);
    }

    public void addProfile(int userId, int profileTypeId) {
        Profile profile = new Profile();
        profile.setId(new ProfileId(new Integer(profileTypeId).shortValue(), userId));
        profile.setRecordDate(Calendar.getInstance().getTime());

        session.save(profile);
    }

    public void deleteProfile(int userId, int profileTypeId) {
        Profile profile = new Profile();
        profile.setId(new ProfileId(new Integer(profileTypeId).shortValue(), userId));

        session.delete(profile);

        if (new Integer(profileTypeId).shortValue() == PROFILE_TYPE_STUDENT || new Integer(profileTypeId).shortValue() == PROFILE_TYPE_TUTOR) {
            session.createQuery("delete from UserData "
                    + "where id.account = :userId "
                    + "and id.profileType = :profileType").setParameter("userId", userId).setParameter("profileType", new Integer(profileTypeId).shortValue()).executeUpdate();
        }

        if (new Integer(profileTypeId).shortValue() == PROFILE_TYPE_STUDENT) {
            session.createQuery("delete from WorkItem wi "
                    + "where wi.id.id = :userId").setParameter("userId", userId).executeUpdate();
        }
        session.getTransaction().commit();
    }

    public boolean changePassword(int userId, String oldPass, String newPass) throws NoSuchAlgorithmException {
        boolean changed = false;
        String oldEncrypt = CreateJDBCRealmUser.encryptPassword(oldPass);
        UserIdentity ui = (UserIdentity) session.get(UserIdentity.class, userId);
        if (ui.getPassword().equals(oldEncrypt)) {
            ui.setPassword(CreateJDBCRealmUser.encryptPassword(newPass));
            session.save(ui);
            changed = true;
        }
        return changed;
    }

    public List<UserIdentity> findAllUsers() {
        return session.createQuery("from UserIdentity ORDER BY name").list();
    }
    /*
    public List<UserIdentity> findUsersByStudent(int currentUserId) {
    //return session.createQuery("").list();
    throw new UnsupportedOperationException("Not yet implemeted");
    }
     */
}
